using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;


namespace LoRel {

    [TestClass]
    public class TestExclusiveSubsets {

        private Core PrepareCore()
        {
            Core core = new Core();
            core.DeclareField("Name", typeof(String));
            core.DeclareField("DepName", typeof(String));
            core.DeclareField("DepID", typeof(int));
            core.DeclareField("Month", typeof(String));
            
            core.DeclareColumn("Emp", "Name");
            core.DeclareColumn("Emp", "DepID");

            core.DeclareColumn("Dep", "DepID");
            core.DeclareColumn("Dep", "DepName");

            core.DeclareColumn("Mo", "DepID");
            core.DeclareColumn("Mo", "Month");
            return core;
        }

        [TestMethod]
        public void TestExclusiveSubset() {
            Core core = PrepareCore();

            ColumnReference emp_name_col = core.GetColumn("Emp", "Name");
            ColumnReference emp_depid_col = core.GetColumn("Emp", "DepID");
            ColumnReference dep_depid_col = core.GetColumn("Dep", "DepID");
            ColumnReference dep_depname_col = core.GetColumn("Dep", "DepName");

            Key Sam = new Key(NullReport.AllPopulated, "Sam", 1);
            Key Jim = new Key(NullReport.AllPopulated, "Jim", 1);
            Key Jill = new Key(NullReport.AllPopulated, "Jill", 1);
            Key Tim = new Key(NullReport.AllPopulated, "Tim", 1);

            Key A = new Key(NullReport.AllPopulated, 1, "A");
            Key B = new Key(NullReport.AllPopulated, 1, "B");
            Key C = new Key(NullReport.AllPopulated, 1, "C");

            Key index_key_person = new Key( NullReport.AllPopulated, 1 );
            Key index_key_dept = new Key( NullReport.AllPopulated, 1 );


            IndexBucket person_bucket = new IndexBucket( index_key_person );
            IndexBucket dept_bucket = new IndexBucket( index_key_dept );

            Key person_key = new Key(NullReport.AllNull, null, null);
            Key dept_key = new Key(NullReport.AllNull, null, null);

            CombinedKeySchema schema = new CombinedKeySchema( 
                new List< ColumnReference >(){ emp_name_col, emp_depid_col, dep_depid_col, dep_depname_col }                , 
                new List< Key >(){ person_key, dept_key }
                );

            BucketTransaction transaction = new BucketTransaction( new Key( NullReport.AllPopulated, 1 ), 2  );
            transaction.ReceiveCompleteBucket( 0, person_bucket );
            transaction.ReceiveCompleteBucket(1, dept_bucket);
            UnderlyingExclusiveSubset subset = new UnderlyingExclusiveSubset(
                    schema, 
                    -1,
                    transaction
                );

            List<Key> correct_results = new List< Key >();
            correct_results.Add( new Key( NullReport.AllPopulated, "Sam", 1, 1, "A" ) );

            List<Key> output_results = new List< Key >();
            subset.UnderlyingKeyAdded += delegate( SubsetKey key ) { output_results.Add( key ); };

            // begin updates
            person_bucket.EnqueueUpdate( GetAdditionArgs( Sam ) );
            dept_bucket.EnqueueUpdate( GetAdditionArgs( A ) );
            Assert.IsTrue( correct_results.SequenceEqual( output_results ) );

            // add two additional people
            person_bucket.EnqueueUpdate(GetAdditionArgs(Jim));
            person_bucket.EnqueueUpdate(GetAdditionArgs(Jill));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "A"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jill", 1, 1, "A"));

            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            // add a single department, resulting in 3 person rows produced in the join.
            dept_bucket.EnqueueUpdate( GetAdditionArgs( B ) );
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "B"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "B"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jill", 1, 1, "B"));

            // Just make sure that continues to work on other side.
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
            dept_bucket.EnqueueUpdate(GetAdditionArgs(C));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "C"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "C"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jill", 1, 1, "C"));

            person_bucket.EnqueueUpdate(GetAdditionArgs(Tim));
            correct_results.Add(new Key(NullReport.AllPopulated, "Tim", 1, 1, "A"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Tim", 1, 1, "B"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Tim", 1, 1, "C"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
            Assert.IsTrue(subset.SubsetKeys.SequenceEqual(output_results));

        }

        public void TestSubsetAllowsNull() {
            throw new NotImplementedException("Need to implement 'LEFT JOIN + right is null' style operation");
        }


        private TableChangeArgs GetAdditionArgs( Key key ) {
            TableChangeArgs args = new TableChangeArgs( ChangeType.Add, 1, new[]{GetAdditionItem( key )} );
            return args;
        }

        private TableChangeItem GetAdditionItem( Key key ) {
            TableChangeItem item = new TableChangeItem( new TableRow( key, null ), null );
            return item;
        }

    }
}